﻿using NAudio.Wave;
using System;


namespace TImSdk
{
    public class NAudioService
    {
        private static object lockobj = new object();
        private static NAudioService mInstance;
        private static WaveOut waveOut { get; set; }
        public WaveInEvent mWavIn { get; set; }
        public WaveFileWriter mWavWriter { get; set; }
        public ISoundRecordCallback _soundRecordCallback { get; set; }
        public static NAudioService GetInstance
        {
            get
            {
                if (mInstance == null)
                {
                    lock (lockobj)
                    {
                        if (mInstance == null)
                        {
                            mInstance = new NAudioService();
                        }
                    }
                }
                return mInstance;
            }
        }
        private const int MaxTime = 60;

        
        /// <summary>
        /// 完成之后要做的事情
        /// </summary>
        private Action stoppedAction { get; set; }
        /// <summary>
        /// 播放声音
        /// </summary>
        /// <param name="soundPath"></param>
        public void PalySound(string soundPath, Action action = null)
        {
            try
            {
                Close();
                waveOut = new WaveOut();
                var audioFileReader = new AudioFileReader(soundPath);
                waveOut.Init(audioFileReader);
                waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
                stoppedAction = action;
                waveOut.Play();
            }
            catch (Exception)
            {
                Close();
                action?.Invoke();
            }

        }
        /// <summary>
        /// 关闭
        /// </summary>
        private void Close()
        {
            if (waveOut != null)
            {
                stoppedAction?.Invoke();
                waveOut?.Stop();
                waveOut.PlaybackStopped -= WaveOut_PlaybackStopped;
                waveOut?.Dispose();
                waveOut = null;
                stoppedAction = null;
            }
        }
        /// <summary>
        /// 停止时事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WaveOut_PlaybackStopped(object sender, StoppedEventArgs e)
        {
            stoppedAction?.Invoke();
        }



        /// <summary>
        /// 开始录音
        /// </summary>
        /// <param name="filePath"></param>
        public int StartRecord(string filePath)
        {
            try
            {
                mWavIn = new WaveInEvent();
                mWavIn.WaveFormat = new WaveFormat(4000, 8, 1);

                mWavIn.DataAvailable += MWavIn_DataAvailable;
                // mWavIn.RecordingStopped += MWavIn_RecordingStopped; 有冲突
                mWavWriter = new WaveFileWriter(filePath, mWavIn.WaveFormat);
                mWavIn.StartRecording();
                return 0;
            }
            catch (Exception)
            {
                StopRecord(false);
                return -1;
            }
           
        }

        /// <summary>
        /// 停止录音
        /// </summary>
        public void StopRecord(bool needCallBack=true)
        {
            if(needCallBack)
                _soundRecordCallback?.OnStopRecordingCallabck(mWavWriter.Filename, mWavWriter.Length, mWavWriter.TotalTime.Seconds);
            if (mWavIn != null)
            {
                mWavIn?.StopRecording();
                mWavIn?.Dispose();
                mWavIn = null;
            }
            if (mWavWriter != null)
            {
                mWavWriter?.Close();
                mWavWriter = null;
            }
        }
        /// <summary>
        /// 最多60s
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MWavIn_DataAvailable(object sender, WaveInEventArgs e)
        {
            mWavWriter.Write(e.Buffer, 0, e.BytesRecorded);
            if (mWavWriter.Position > mWavIn.WaveFormat.AverageBytesPerSecond * MaxTime)
            {
                StopRecord(); 
            }

        }

        ///****************
        // * https://blog.csdn.net/luols/article/details/38750907
        // * **********/
        //private const string Winmm = "winmm.dll";

        //[DllImport(Winmm, EntryPoint = "mciSendString", CharSet = CharSet.Auto)]
        //public static extern int MciSendString(string lpstrCommand, string lpstrReturnString, int uReturnLength, int hwndCallback);
        //private readonly Stopwatch _stopwatch = new Lazy<Stopwatch>(() => new Stopwatch()).Value;
        ///// <summary>
        ///// 开始录音
        ///// </summary>
        //public int StartRecordSound()
        //{
        //    try
        //    {
        //        var result = MciSendString("open new Type waveaudio Alias movie", "", 0, 0);
        //        result = MciSendString("set movie bitspersample 16", "", 0, 0);
        //        result = MciSendString("set movie samplespersec 16000", "", 0, 0);
        //        result = MciSendString("set movie channels 1", "", 0, 0);
        //        result = MciSendString("set movie alignment 2", "", 0, 0);
        //        //int alignment = bitspersample * channels / 8; //每个时刻样本的字节数
        //        result = MciSendString("record movie", "", 0, 0);
        //        return result;
        //    }
        //    catch (Exception)
        //    {
        //        return -1;
        //    }

        //}
        ///// <summary>
        ///// 停止录音
        ///// </summary>
        ///// <param name="saveSoundPath"></param>
        ///// <returns></returns>
        //public int StopRecordSound(string saveSoundPath)
        //{
        //    try
        //    {
        //        var result = MciSendString("stop movie", "", 0, 0);
        //        Console.WriteLine($"stop movie:{result}");
        //        result = MciSendString($"save movie {saveSoundPath}", "", 0, 0);
        //        Console.WriteLine($"save movie:{result}");
        //        result = MciSendString("close movie", "", 0, 0);
        //        Console.WriteLine($"close movie:{result}");
        //        return result;
        //    }
        //    catch (Exception)
        //    {
        //        return -1;
        //    }

        //}
    }
}
